﻿using System.Drawing;
using FlowLib.Utils.Connection;
using System.Windows.Forms;

namespace ClientExample.Controls
{
    public class ConnectionDetection : ExpandablePanel
    {
        protected delegate void Function(Detect.Functions func);

        protected ConnectionIndication indication = new ConnectionIndication();
        protected Detect detect = new Detect();
        protected bool bManual = false;
        protected string title = string.Empty;
        protected string status = string.Empty;

        public bool ManualDetect
        {
            get { return bManual; }
            set
            {
                bManual = value;
                Reset();
                indication.btnTest.Visible = bManual;
                indication.numUpDwnPort.Visible = bManual;
                indication.lblPort.Visible = bManual;
            }
        }

        public int Mode
        {
            get
            {
                return detect.ConnectionType;
            }
        }

        public new string Text
        {
            get { return title; }
            set 
            {
                title = value;
                SetStatus(status);
            }
        }

        public ConnectionDetection()
        {
            label1.BackColor = Color.White;
            splitContainer1.Panel1.BackColor = Color.White;
            detect.ProgressChanged += new Detect.ProgressChange(detect_ProgressChanged);
            detect.SuccessChanged += new Detect.ProgressChange(detect_SuccessChanged);

            this.ExpandedChanged += new System.EventHandler<FlowLib.Events.FmdcEventArgs<int, object>>(ConnectionDetection_ExpandedChanged);
            Panel.Controls.Add(indication);
            indication.Dock = System.Windows.Forms.DockStyle.Fill;
            indication.btnTest.Click += new System.EventHandler(btnTest_Click);
            ManualDetect = false;
        }

        protected void Reset()
        {
            // Stop
            detect.Stop();
            SetStatus(string.Empty);
            indication.btnInternet.Enabled = false;
            indication.btnInternet.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
            indication.btnLoopback.Enabled = false;
            indication.btnLoopback.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
            indication.btnNAT.Enabled = false;
            indication.btnNAT.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
            indication.btnPublicIP.Enabled = false;
            indication.btnPublicIP.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
            indication.btnUPnP.Enabled = false;
            indication.btnUPnP.ForeColor = Color.FromKnownColor(KnownColor.ControlText);
            indication.btnTest.Enabled = true;
        }

        protected void SetStatus(string newstatus)
        {
            if (!string.IsNullOrEmpty( newstatus))
                TopLabel.Text = string.Format("{0} - {1}", title ,newstatus);
            else
                TopLabel.Text = title;
        }

        void btnTest_Click(object sender, System.EventArgs e)
        {
            Reset();
            detect.Port = decimal.ToInt32(indication.numUpDwnPort.Value);
            // Start
            detect.Start();
        }

        void detect_SuccessChanged(Detect sender, Detect.Functions prog)
        {
            if (!this.IsDisposed)
            {
                Invoke(new Function(OnSuccessChanged), prog);
            }
        }

        void detect_ProgressChanged(Detect sender, Detect.Functions prog)
        {
            if (!this.IsDisposed)
            {
                Invoke(new Function(OnProgressChanged), prog);
            }
        }

        protected void OnSuccessChanged(Detect.Functions prog)
        {
            if ((Detect.Functions.InternetAccess & prog) == Detect.Functions.InternetAccess)
                indication.btnInternet.ForeColor = Color.Green;
            if (
                (Detect.Functions.InternalListenAccess & prog) == Detect.Functions.InternalListenAccess
                && (Detect.Functions.InternalRecieveAccess & prog) == Detect.Functions.InternalRecieveAccess
                )
                indication.btnLoopback.ForeColor = Color.Green;
            if ((Detect.Functions.ExternalIp & prog) == Detect.Functions.ExternalIp)
                indication.btnPublicIP.ForeColor = Color.Green;
            if (
                ((Detect.Functions.ExternalRecieveAccess & prog) == Detect.Functions.ExternalRecieveAccess
                && (Detect.Functions.InternalListenAccess & prog) == Detect.Functions.InternalListenAccess
                && (Detect.Functions.InternalRecieveAccess & prog) == Detect.Functions.InternalRecieveAccess
                && (Detect.Functions.ExternalIp & prog) == Detect.Functions.ExternalIp
                )
                || (Detect.Functions.UPnPExternalRecieveAccess & prog) == Detect.Functions.UPnPExternalRecieveAccess
                )
                indication.btnNAT.ForeColor = Color.Green;
            if (
                (Detect.Functions.UPnPDevices & prog) == Detect.Functions.UPnPDevices
                && (Detect.Functions.UPnPIGD & prog) == Detect.Functions.UPnPIGD
                && (Detect.Functions.UPnPExternalIp & prog) == Detect.Functions.UPnPExternalIp
                && (Detect.Functions.UPnPAddMapping & prog) == Detect.Functions.UPnPAddMapping
                && (Detect.Functions.UPnPGetMapping & prog) == Detect.Functions.UPnPGetMapping
                && (Detect.Functions.UPnPDeleteMapping & prog) == Detect.Functions.UPnPDeleteMapping
                )
                indication.btnUPnP.ForeColor = Color.Green;

            if (MigthBeActiveWithUPnP())
                indication.btnNAT.ForeColor = Color.Orange;
        }

        /// <summary>
        /// Determin if user migth be able to be active through UPnP even if not everything seems to have gone OK.
        /// This could mean that user router doesnt support loopback on public ip
        /// </summary>
        /// <returns></returns>
        protected bool MigthBeActiveWithUPnP()
        {
            Detect.Functions prog = detect.Success;
            // Is UPnP OK?
            if (
                (Detect.Functions.UPnPDevices & prog) == Detect.Functions.UPnPDevices
                && (Detect.Functions.UPnPIGD & prog) == Detect.Functions.UPnPIGD
                && (Detect.Functions.UPnPExternalIp & prog) == Detect.Functions.UPnPExternalIp
                && (Detect.Functions.UPnPAddMapping & prog) == Detect.Functions.UPnPAddMapping
                && (Detect.Functions.UPnPGetMapping & prog) == Detect.Functions.UPnPGetMapping
                && (Detect.Functions.UPnPDeleteMapping & prog) == Detect.Functions.UPnPDeleteMapping
                )
            {
                // Is everything ok except ExternalReceiveAccess? (This could mean that user router doesnt support loopback on public ip).
                if (
                    ((Detect.Functions.ExternalRecieveAccess & prog) != Detect.Functions.ExternalRecieveAccess
                    && (Detect.Functions.InternalListenAccess & prog) == Detect.Functions.InternalListenAccess
                    && (Detect.Functions.InternalRecieveAccess & prog) == Detect.Functions.InternalRecieveAccess
                    && (Detect.Functions.ExternalIp & prog) == Detect.Functions.ExternalIp
                    )
                    || (Detect.Functions.UPnPExternalRecieveAccess & prog) == Detect.Functions.UPnPExternalRecieveAccess
                    )
                {
                    return true;
                }
            }
            return false;
        }

        protected void OnProgressChanged(Detect.Functions prog)
        {
            switch (prog)
            {
                case Detect.Functions.Start:
                    indication.btnTest.Enabled = false;
                    SetStatus("Working ...");
                    break;
                case Detect.Functions.InternetAccess:
                    indication.btnInternet.Enabled = true;
                    break;
                case Detect.Functions.InternalListenAccess:
                    indication.btnLoopback.Enabled = true;
                    break;
                case Detect.Functions.InternalRecieveAccess:
                    indication.btnLoopback.Enabled = true;
                    break;
                case Detect.Functions.ExternalIp:
                    indication.btnPublicIP.Enabled = true;
                    break;
                case Detect.Functions.ExternalRecieveAccess:
                    indication.btnNAT.Enabled = true;
                    break;
                case Detect.Functions.UPnPDevices:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.UPnPIGD:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.UPnPExternalIp:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.UPnPAddMapping:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.UPnPGetMapping:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.UPnPDeleteMapping:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.UPnPExternalRecieveAccess:
                    indication.btnUPnP.Enabled = true;
                    break;
                case Detect.Functions.End:
                    SetStatus("Finished");
                    indication.btnTest.Enabled = true;

                    Program.Settings.ConnectionMode = detect.ConnectionType;
                    // Check if detect say we need to be passive
                    if (detect.ConnectionType == 1)
                    {
                        if (MigthBeActiveWithUPnP())
                        {
                            if (MessageBox.Show("Your router seem to support UPnP but I have problems to connect back to your computer using your public IP.\r\nSome routers have a known issue that will result in this error.\r\nYou might be able to be active anyway. Do you want to try to be active?", "Do you want to try to be active?", MessageBoxButtons.YesNo) == DialogResult.Yes)
                            {
                                Program.Settings.ConnectionMode = 4;
                            }
                        }
                    }
                    
                    if (detect.ExternalIP != null)
                        Program.Settings.IP = detect.ExternalIP.ToString();
                    Program.Settings.TcpPort = detect.Port;
                    if (detect.InternalIP != null)
                        Program.Settings.InternalIP = detect.InternalIP.ToString();


                    break;
            }
        }

        void ConnectionDetection_ExpandedChanged(object sender, FlowLib.Events.FmdcEventArgs<int, object> e)
        {
            //if (e.Action > this.Height)
            if (!Expanded)
            {
                // HIDE
                Reset();
            }
            else
            {
                // SHOW
                if (!ManualDetect)
                {
                    detect.Start();
                }
                if (ManualDetect)
                {
                    this.Height = splitContainer1.Panel1MinSize + 77;
                }
                else
                {
                    this.Height = splitContainer1.Panel1MinSize + 58;
                }
            }
        }

        protected override void ChangeImage()
        {
            if (Expanded)
                pictureBox1.Image = Program.Images.GetBitmap("Select.png");
            else
                pictureBox1.Image = Program.Images.GetBitmap("Unselect.png");
        }
    }
}
